<?php
/*------------------------------------------------------------------------------
Copyright (C) 2006 J.Ducastel <jducastel@jducastel.fr>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
------------------------------------------------------------------------------*/

/**
* datetime instance handler
* @author J.Ducastel <jducastel@jducastel.fr>
* @version 
*/
class dateTimeBean extends dataBean {
/*------------------------------------------------------------------------------
                                                                         members
------------------------------------------------------------------------------*/
	/**
	* @var int year value
	*/
	var $year=0;
	function _year_required() { return true; }
	function _year_inputfilter($x) { return intval($x); }
	function _year_check($x) { return is_int($x); }
	
	/**
	* @var int month number 1-12
	*/
	var $mon=1;
	function _mon_required() { return true; }
	function _mon_inputfilter($x) { return intval($x); }
	function _mon_check($x) {
		return is_int($x) and $x>0 and $x<13;
	}
	
	/**
	* @var month's day number 1-31
	*/
	var $mday=1;
	function _mday_required() { return true; }
	function _mday_inputfilter($x) { return intval($x); }
	function _mday_check($x) {
		return is_int($x) and $x>0 and $x<=$this->daysInMonth();
	}/**/
	
	//function _yday_get() { return $this->yearDay(); }
	
	/**
	* @var hours 0-23
	*/
	var $hours=0;
	function _hours_required() { return true; }
	function _hours_inputfilter($x) { return intval($x); }
	function _hours_check($x) {
		return is_int($x) and $x>=0 and $x<25;
	}
	
	/**
	* @var minutes 0-59
	*/
	var $minutes=0;
	function _minutes_required() { return true; }
	function _minutes_inputfilter($x) { return intval($x); }
	function _minutes_check($x) {
		return is_int($x) and $x>=0 and $x<60;
	}
	
	/**
	* @var seconds 0-59
	*/
	var $seconds=0;
	function _seconds_required() { return true; }
	function _seconds_inputfilter($x) { return intval($x); }
	function _seconds_check($x) {
		return is_int($x) and $x>=0 and $x<60;
	}
	
	/**
	* @var string en|fr
	*/
	var $locale='en';
	function _locale_check($x) {
		return in_array($x,array('en','fr'));
	}
	
/*------------------------------------------------------------------------------
                                                                   global config
------------------------------------------------------------------------------*/
	/**
	* @var bool enable/disable set() and loadData().
	*/
	//var $_databean_lock_members=false;
	/**
	* @var bool enable/disable loadStructure() and lockStructure().
	*/
	//var $_databean_lock_structure=true;
	
	
	var $_datetimebean_tostring_default='%Y%m%dT%H:%i:%s'; // default toString format
	
/*------------------------------------------------------------------------------
                                                                     constructor
------------------------------------------------------------------------------*/
	/**
	* constructor alias
	* @access public
	*/
	function dateTimeBean($input=null,$format=null) {
		$this->__construct($input,$format);
	}/**/
	
	/**
	* real constructor
	* @access public
	*/
	function __construct($input=null,$format=null) {
		//$this->structureLock();
		if (is_null($input))
			$input=getdate();
		$this->_datetimebean_setfrominput($input,$format);
	}/**/
/*------------------------------------------------------------------------------
                                                                  public methods
------------------------------------------------------------------------------*/
	/**
	* getter/setter for year
	* @access public
	* @param int $new optional
	* @return bool|int bool if $new provided, else current value
	*/
	function year($new=null) {
		if (!is_null($new))
			return $this->set('year');
		return $this->get('year');
	}/**/
	
	/**
	* getter/setter for mon
	* @access public
	* @param int $new optional
	* @return bool|int bool if $new provided, else current value
	*/
	function mon($new=null) {
		if (!is_null($new))
			return $this->set('mon');
		return $this->get('mon');
	}/**/
	
	/**
	* return month name with current locale
	* @param
	* @access public
	* @return 
	*/
	function monthName() {
		switch ($this->locale) {
			case 'fr':
				$months=array('','Janvier','Fevrier','Mars','Avril','Mai','Juin','Juillet','Aout','Septembre','Octobre','Novembre','Decembre');
				break;
			case 'en':
			default:
				$months=array('','January','February','March','April','May','June','July','August','September','October','November','December');
		}
		return $months[$this->mon];
	}/**/
	
	/**
	* getter/setter for mday
	* @access public
	* @param int $new optional
	* @return bool|int bool if $new provided, else current value
	*/
	function mday($new=null) {
		if (!is_null($new))
			return $this->set('mday');
		return $this->get('mday');
	}/**/

	/**
	* return week day's name with current locale
	* @param
	* @access public
	* @return 
	*/
	function dayName() {
		switch ($this->locale) {
			case 'fr':
				$days=array('Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi');
				break;
			case 'en':
			default:
				$days=array('Sunday','Monday','Tuesday','Wednesday','Tursday','Friday','Saturday');
		}
		return $days[$this->weekDay()-1];
	}/**/
	
	/**
	* sets all values to 0
	* access public
	* @return bool
	* /
	function reset() {
		$this->year=0;
		$this->mon=1;
		$this->mday=1;
		$this->hours=0;
		$this->minutes=0;
		$this->seconds=0;
		return true;
	}/**/
	
	/**
	* return instance for now
	* @access public
	* @static
	* @return datetimebean
	*/
	function now() {
		return new datetimebean(getdate(),'php_getdate');
	}/**/
	
	/**
	* sets from various input such as string, getdate array, timestamp
	* @access public
	* @param mixed $input
	* @param string $format input format, wil guessed if null
	* @return bool
	*/
	function setFromInput($input,$format=null) {
		return $this->_dateTimeBean_setFromInput($input,$format);
	}
	
	/**
	* auto-corrects invalid dates, ie values within invalid ranges
	* if some value is outside range, it will crawl through time
	* @access public
	* @return bool
	*/
	function autoCorrect() {
		// seconds correction
		if (!$this->_correctSeconds())
			return false;
		// minutes correction
		if (!$this->_correctMinutes())
			return false;
		// hours correction
		if (!$this->_correctHours())
			return false;
		// mday correction
		if (!$this->_correctMday())
			return false;
		// month correction
		if (!$this->_correctMon())
			return false;
		return true;
	}/**/
	
	/**
	* return date as string, upon a format as in mysql date_formaty
	* %% => %
	* %a weekday name 3 letters
	* %b month name, 3 letters
	* %d, %e month day, 01..31
	* %H, %k hours, 00..23
	* %h, %I, %l hours, 00..12
	* %i minutes, 00..60
	* %j, year day, 001..366
	* %m month, 01..12
	* %M month name
	* %p AM ou PM
	* %s, %S seconds 00..59
	* %U week number, starting on sunday  00..53
	* %u week number, starting on monday  00..53
	* %W week day name
	* %w week day number starting on sunday, 0..6 
	* %y year on 2 digits
	* %Y year on 4 digits
	* @param string $format as in mysql DATE_FORMAT default YmdTH:i:s (iso8601)
	* @access public
	* @return string
	* @todo : D,j,l,N,s,w,z,W,h etc
	*/
	function toString($format=null) {
		if (!is_string($format))
			$format=$this->_datetimebean_tostring_default;
		$format=str_replace('%Y',sprintf('%04d',$this->year),$format);
		$format=str_replace('%y',sprintf('%02d',$this->year),$format);
		$format=str_replace('%m',sprintf('%02d',$this->mon),$format);
		$format=str_replace('%d',sprintf('%02d',$this->mday),$format);
		$format=str_replace('%H',sprintf('%02d',$this->hours),$format);
		$format=str_replace('%i',sprintf('%02d',$this->minutes),$format);
		$format=str_replace('%s',sprintf('%02d',$this->seconds),$format);
		$format=str_replace('%j',sprintf('%03d',$this->yearDay()),$format);
		$format=str_replace('%w',sprintf('%01d',$this->weekDay()),$format);
		$format=str_replace('%M',$this->monthName(),$format);
		$format=str_replace('%W',$this->dayName(),$format);
		return $format;
	}/**/

	/**
	* to mysql date format = %Y-%m-%d
	* @access public
	* @return string
	*/
	function toMysqlDate() {
		return $this->toString('%Y-%m-%d');
	}/**/

	/**
	* to mysql datetime format = %Y-%m-%d %H:%i:%s
	* @access public
	* @return string
	*/
	function toMysqlDateTime() {
		return $this->toString('%Y-%m-%d %H:%i:%s');
	}/**/

	/**
	* to ISO8601 format = %Y-%m-%dT%H:%i:%s
	* see http://www.cl.cam.ac.uk/~mgk25/iso-time.html
	* @param
	* @access public
	* @return 
	*/
	function toIso8601() {
		return $this->toString('%Y-%m-%dT%H:%i:%s');
	}/**/

	/**
	* for xml-rpc usage %Y%m%dT%H:%i:%s
	* @access public
	* @return string
	*/
	function toXmlRpc() {
		return $this->toString('%Y%m%dT%H:%i:%s');
	}/**/

	/**
	* formated as french date %d/%m/%Y
	* @access public
	* @return string
	*/
	function toFrDate() {
		return $this->toString('%d/%m/%Y');
	}/**/

	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function toString($format='') {
		
	}/**/
	
	/**
	* return as timestamp
	* @access public
	* @return int
	*/
	function toTimestamp() {
		return mktime(
			$this->hours
			, $this->minutes
			, $this->seconds
			, $this->mon
			, $this->mday
			, $this->year);
	}/**/
	
	/**
	* return as days count since date 0000-00-00
	* invalid for dates before
	* @access public
	* @return int
	*/
	function toDays() {
		$days=0;
		// years days
		$days+=$this->year*365.25;
		// past days in year
		$days+=$this->yearDay();
		return $days;
	}/**/
	
	/**
	* return as seconds count since date 0000-00-00 00:00:00
	* invalid for dates before
	* @access public
	* @return int
	*/
	function toSeconds() {
		$seconds=0;
		// years seconds
		$seconds+=$this->year*31556926;
		// past days in year seconds
		$seconds+=($this->yearDay()-1)*86400;
		// past hours in current day seconds
		$seconds+=$this->hours*3600;
		// past minutes in current hours seconds
		$seconds+=$this->minutes*60;
		// current seconds
		$seconds+=$this->seconds;
		return $seconds;
	}/**/
	
	/**
	* add some time to the datetimebean.
	* accepts integers as seconds, or duration instance
	* @param int $input seconds, or duration instance
	* @access public
	* @return bool
	*/
	function add($input) {
		// converting input to seconds
		if (is_a($input,'datetimebean')) {
			$input=$input->toSeconds();
		} else if (is_a($input,'duration')) {
			$input=$input->getAsSeconds();
		} else if (!is_integer($input)) {
			trigger_error('dateTimeBean->add(): incorrect input format');
			return false;
		}
		// adding seconds
		$this->addSeconds($input);
		return $this->_correctSeconds();
	}/**/
	
	/**
	* adds some years to current date
	* @access public
	* @param int $val years to add
	* @return bool
	*/
	function addYears($val) {
		if (!is_integer($val)) {
			return false;
		}
		$this->year+=$val;
		return true;
	}/**/
	
	/**
	* adds some days to current date
	* @access public
	* @param int $val days to add
	* @return bool
	*/
	function addDays($val) {
		if (!is_integer($val)) {
			trigger_error('dateTimeBean->addDays(): must be integer');
			return false;
		}
		// storing current mday for backup
		$old=$this->mday;
		// going wild
		$this->mday+=$val;
		// correcting invalid dates
		if ($this->autocorrect())
			return true;
		// back to old value
		$this->mday=$old;
		return false;
	}/**/
	
	/**
	* adds some years to current date
	* @access public
	* @param int $val seconds to add
	* @return bool
	*/
	function addSeconds($val) {
		if (!is_integer($val)) {
			trigger_error('dateTimeBean->addSeconds(): must be integer');
			return false;
		}
		// storing current mday for backup
		$old=$this->seconds;
		// going wild
		$this->seconds+=$val;
		// echo wakapi::vardump($this);
		// correcting invalid dates
		if ($this->_correctSeconds())
			return true;
		// back to old value
		$this->seconds=$old;
		return false;
	}/**/
	
	/**
	* soustracts some time to the date.
	* First convert input into another datetimebean instance, then soustracts its time to the date
	* @access public
	* @param mixed $date
	* @return bool
	* /
	function soustract($input,$format=NULL) {
		if (!is_a($input,'datetimebean')) {
			// if integer, avoid considering timestamp
			if (is_int($input) and is_null($format))
				$format='seconds';
			// building new instance
			$input=&new datetimebean($input,$format);
		}
		// cascade soustraction
		if (($this->seconds-=$input->seconds)<0) {
			$this->seconds+=60; $this->minutes-=1;
		}
		if (($this->minutes-=$input->minutes)<0) {
			$this->minutes+=60; $this->hours-=1;
		}
		if (($this->hours-=$input->hours)<0) {
			$this->hours+=24; $this->mday-=1;
		}
		if (($this->mday-=$input->mday)<1) {
			$this->mon-=1; $this->mday+=$this->daysInMonth();
		}
		if (($this->mon-=$input->mon)<1) {
			$this->mon+=12; $this->year-=1;
		}
		$this->year-=$input->year;
		return true;
	}/**/
	
	/**
	* computes days count since/before another date
	* positive if comparing to a date before itself
	* @param datetimebean|string|int date to compare to
	* return int
	*/
	function daysDistance($other) {
		if (!is_a($other,'datetimebean'))
			$other=new datetimebean($other);
		// echo '<p>'.$this->tomysqldate().' daysDistance with '.$other->tomysqldate().'</p>';
		// $dist=0;
		// comparing to past or future ?
		if ($this->isAfter($other)) {
			// comparing to past
			// same year ?
			if ($this->year==$other->year()) {
				// just soustracting other yearDay to self yearDay
				$dist=$this->yearDay()-$other->yearDay();
			} else {
				// days between other date and end of its year
				$dist=1+$other->daysInYear()-$other->yearDay(); //echo "<p>$dist</p>";
				// adding full years between other date and self
				for ($year=$other->year()+1; $year<$this->year(); $year++) {
					$dist+=dateTimeBean::daysInYear($year);
				}
				// adding days since start of self year
				$dist+=$this->yearDay();
			}
		} else {
			// comparing to future, this will give a negative count
			// same year ?
			if ($this->year==$other->year()) {
				// just soustracting other yearDay to self yearDay
				$dist=$this->yearDay()-$other->yearDay();
			} else {
				// days until end of year
				$dist=1+$this->daysInYear()-$this->yearDay();
				// adding full years between self and other date
				for ($year=$this->year()+1; $year<$other->year(); $year++) {
					$dist+=dateTimeBean::daysInYear($year);
				}
				// adding other date yearday
				$dist+=$other->yearDay();
			}
		}
		return $dist;
	}/**/
	
	/**
	* returns days count in month
	* may be used static
	* @access public
	* @param int year if null, bean's year is taken
	* @param int mon if null, bean's mon is taken
	* @return int
	*/
	function daysInMonth($year=null,$mon=null) {
		if (is_null($year) and $this)
			$year=$this->year;
		if (is_null($mon) and $this)
			$mon=$this->mon;
		if ($mon==2) {
			if ($year%4) return 28;
			elseif ($year%100) return 29;
			elseif ($year%1000) return 28;
			else return 29;
		} elseif ($mon==4 or $mon==6 or $mon==9 or $mon==11) return 30;
		return 31;
	}/**/
	
	/**
	* return days count in year
	* may be used static
	* @access public
	* @param int year
	* @return int
	*/
	function daysInYear($year=null) {
		if (is_null($year) and isset($this)) {
			$year=$this->year;
		}
		return 337 + datetimebean::daysInMonth($year,2);
	}/**/
	
	/**
	* returns year's day = days count from year start
	* @access public
	* @return int
	*/
	function yearDay() {
		$year=$this->year();
		$mon=$this->mon();
		// days for past months
		for ($n=1; $n<$mon; $n++) {
			$yday+=$this->daysInMonth($year,$n);
		}
		// days since month start
		$yday+=$this->mday;
		return $yday;
	}/**/
	
	/**
	* return week day, starting on sunday=0
	* use Mike Keith algorithm
	* @access public
	* @return int
	*/
	function weekDay() {
		$year=$this->year;
		$mon=$this->mon;
		$mday=$this->mday;
		// 
		$z=($mon>=3)?$year:$year-1;
		$D=floor((23*$mon)/9);
		$D+=$mday;
		$D+=4;
		$D+=$year;
		$D+=floor($z/4);
		$D-=floor($z/100);
		$D+=floor($z/400);
		if ($mon>=3)
			$D-=2;
		$wday=$D%7;
		return intval($wday);
	}/**/
	
	/**
	* return week number, ISO 8601
	* http://fr.wikipedia.org/wiki/ISO_8601
	* STILL ONWORK
	* @access public
	* @return int 1-53
	*/
	function weekNumber() {
		// Calculer le jeudi de la même semaine que la date cherchée. On calculera le numéro de semaine de ce jeudi pour être sûr de compter dans la bonne année sans test particulier. (Ce jeudi peut tomber l'année précédente ou suivante, c'est tout l'intérêt.)
		$weekThursday = new datetimebean($this);
		switch ($weekThursday->weekDay()) {
			case 1:
				$weekThursday->addDays(3);
				break;
			case 2:
				$weekThursday->addDays(2);
				break;
			case 3:
				$weekThursday->addDays(1);
				break;
			case 5:
				$weekThursday->addDays(-1);
				break;
			case 6:
				$weekThursday->addDays(-2);
				break;
			case 0:
				$weekThursday->addDays(-3);
				break;
		}
		// echo $weekThursday->toMysqlDate();
		// On considère le 4 janvier de la même année que ce jeudi
		$january4=new datetimebean();
		$january4->set('year',$weekThursday->get('year'));
		$january4->set('mon',1);
		$january4->set('mday',4); //echo '<p>jan 4 : '.$january4->tomysqldate().'</p>';
		// On calcule le lundi de la même semaine que ce 4 janvier (identifiant ainsi le début du comptage; noter que ce lundi peut lui même tomber l'année encore avant: peu importe)
		$startMonday = new datetimebean($january4);
		switch ($january4->weekDay()) {
			case 2:
				$startMonday->addDays(-1);
				break;
			case 3:
				$startMonday->addDays(-2);
				break;
			case 4:
				$startMonday->addDays(-3);
				break;
			case 5:
				$startMonday->addDays(-4);
				break;
			case 6:
				$startMonday->addDays(-5);
				break;
			case 0:
				$startMonday->addDays(-6);
				break;
		}
		//echo '<p>startMonday : '.$startMonday->tomysqldate().'</p>';
		// On calcule le nombre de jours écoulés entre nos deux dates particulières (ce jeudi et ce lundi).
		//echo '<p>weekThursday : '.$weekThursday->toMysqlDate().'</p>';
		$days=$weekThursday->daysDistance($startMonday);
		//echo "<p>$days</p>";
		// On divise par 7 (arrondir à l'entier supérieur). On a le résultat voulu.
		return ceil($days/7);
	}/**/
	
	/**
	* tells if date is in the past
	* @access public
	* @return bool
	*/
	function isPast() {
		return $this->isBefore(datetimebean::now());
	}/**/
	
	/**
	* tells if the date is in the future
	* @access public
	* @return bool
	*/
	function isFuture() {
		return $this->isAfter(datetimebean::now());
	}/**/
	
	/**
	* tells if current datetime is before another datetime
	* @access public
	* @param mixed $input converted to datetimebean if necessary
	* @param string $format date format, will be guessed if necessary
	* @return bool
	*/
	function isBefore($input,$format=null) {
		// converting to datetimebean if required
		if (!is_a($input,'datetimebean'))
			$input=new datetimebean($input,$format);
		// echo '<p>is '.$this->toIso8601().' before '.$input->toiso8601().' ?</p>';
		foreach (array('year','mon','mday','hours','minutes','seconds') as $part) {
			if ($this->$part<$input->$part)
				// no need to continue, is before
				return true;
			if ($this->$part!=$input->$part)
				// no need to continue, is not before
				return false;
			// maybe, continue
		}
		return false;
	}/**/
	
	/**
	* tells if current datetime is after another datetime
	* @access public
	* @param mixed $input converted to datetimebean if necessary
	* @param string $format date format, will be guessed if necessary
	* @return bool
	*/
	function isAfter($input,$format=null) {
		// converting to datetimebean if required
		if (!is_a($input,'datetimebean'))
			$input=new datetimebean($input,$format);
		foreach (array('year','mon','mday','hours','minutes','seconds') as $part) {
			if ($this->$part>$input->$part)
				// no need to continue, is before
				return true;
			if ($this->$part!=$input->$part)
				// no need to continue, is not before
				return false;
			// maybe, continue
		}
		return false;
	}/**/
	
	/**
	* sets self as random date from base within range
	* @param mixed $base datetimebean, or date format. default now
	* @param int range in seconds optional, default 10 years
	* @access public
	* @return bool
	*/
	function setRandom($base=null,$range=null) {
		// setting as base
		if (!is_null($base))
			$this->_datetimebean_setFromInput($base);
		// setting range
		if (!is_numeric($range)) {
			// 10 years
			$range=10*365.25*24*3600;
		}
		$rand=rand(0,intval($range)); //echo $rand;
		$ok=$this->add($rand);
		//echo wakaffvar($this).wakaffvar($base).wakaffvar($range);
		return $ok;
	}/**/
	
	/**
	* sets locale
	* @param string $locale en|fr
	* @access public
	* @return bool
	*/
	function setLocale($locale) {
		return $this->set('locale',$locale);
	}/**/
	
	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function globalCheck() {
		if (!is_int($this->year))
			return false;
		//if (!in_array()
		return true;
	}/**/
	
	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function () {
		
	}/**/
/*------------------------------------------------------------------------------
                                                                 members methods
------------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------
                                                                 private methods
------------------------------------------------------------------------------*/
	/**
	* 
	* @param
	* @access protected
	* @return 
	*/
	function _datetimebean_setFromInput($input,$format=null) {
		// guessing format if necessary
		if (is_null($format))
			$format=$this->_dateTimeBean_guessFormat($input);
		//echo wakdebug::vardump($format);
		// echo wakaffvar($format);
		// searching appropriate method
		$method='_datetimebean_setfrom_'.$format;
		if (!$format or !method_exists($this,$method)) {
			trigger_error('datetimebean: couldn\'t set from input');
			return false;
		}
		return $this->$method($input);
	}/**/
	
		/**
	* defines an input date's format
	* @access private
	* @param mixed $date
	* @return string format : php_timestamp|php_getdate|mysql_date|mysql_datetime|mysql_timestamp
	*/
	function _dateTimeBean_guessFormat(& $input) {
		switch (true) {
			// is already a datetimebean
			case is_a($input,'datetimebean'):
				return 'datetimebean';
				break;
			// php/unix timestamp
			case is_integer($input) and $input>0 and $input<2147483647:
				return 'php_timestamp';
				break;
			// php getdate
			case is_array($input) and isset($input['year']) and isset($input['mon']) and isset($input['mday'])
			and isset($input['hours']) and isset($input['seconds']) and isset($input['minutes']):
				return 'php_getdate';
				break;
			// partial php getdate
			case is_array($input) and isset($input['year']) and isset($input['mon']) and isset($input['mday']):
				return 'php_getdate';
				break;
			// mysql date
			case ereg ('^[0-9]{4}-((0[1-9])|(1[0-2]))-((0[1-9])|([1-2][0-9])|(3[0-1]))$', $input):
				return 'mysql_date';
				break;
			// mysql datetime
			case ereg ('^[0-9]{4}-((0[1-9])|(1[0-2]))-((0[1-9])|([1-2][0-9])|(3[0-1])) '
			.'(([0-1][0-9])|(2[0-3]))(:[0-5][0-9]){2}$', $input):
				return 'mysql_datetime';
				break;
			// mysql timestamp
			case ereg ('^[0-9]{14}$', $input):
				return 'mysql_timestamp';
				break;
			// ISO 8601
			case ereg('^[0-9]{8}T(([0-1][0-9])|(2[0-3]))(:[0-5][0-9]){2}$',$input):
				return 'iso8601';
			// mysql date
			// ce qui suit permet des dates incorrectes type 0000-00-00
			case ereg ('^[0-9]{4}-((0[0-9])|(1[0-2]))-((0[0-9])|([1-2][0-9])|(3[0-1]))$', $input):
				return 'mysql_date';
				break;
			case ereg ('^[0-9]{4}-((0[0-9])|(1[0-2]))-((0[0-9])|([1-2][0-9])|(3[0-1])) '
			.'(([0-1][0-9])|(2[0-3]))(:[0-5][0-9]){2}$', $input):
				return 'mysql_datetime';
				break;
			// "french" usual date dd/mm/yy or dd/mm/yyyy
			case ereg('^((0?[0-9])|([1-2][0-9])|(3[0-1]))/((0?[0-9])|(1[0-2]))/[0-9]{2,4}$', $input):
				return 'fr_date';
				break;
			default:
				trigger_error('date: unkown date format for '.$input.'('.gettype($input).')',E_USER_NOTICE);
				return false;
				break;
		}
	}
	
	/**
	* 
	* @param
	* @access protected
	* @return 
	* /
	function () {
		
	}/**/
/*------------------------------------------------------------------------------
                                                                 private methods
------------------------------------------------------------------------------*/
	/**
	* sets date from another datetimebean (copy)
	* @param datetimebean $bean
	* @return bool
	*/
	function _dateTimeBean_setFrom_datetimebean($bean) {
		if (!is_a($bean,'datetimebean'))
			return false;
		$this->year=$bean->year();
		$this->mon=$bean->mon();
		$this->mday=$bean->mday();
		return true;
	}
	/**
	* sets date from php timestamp format
	* @param int $time
	* @access private
	* @return bool
	*/
	function _dateTimeBean_setfrom_php_timestamp($time) {
		$date=getdate($time);
		return $this->_dateTimeBean_setfrom_php_getdate($date);
	}/**/
	
	/**
	* sets date from php getdate array
	* accepts partial arrays (date only)
	* @param array $date
	* @access private
	* @return bool
	*/
	function _dateTimeBean_setfrom_php_getdate($date) {
		$this->set('year',$date['year']);
		$this->set('mon',$date['mon']);
		$this->set('mday',$date['mday']);
		$this->set('hours',$date['hours']?$date['hours']:0);
		$this->set('minutes',$date['minutes']?$date['minutes']:0);
		$this->set('seconds',$date['seconds']?$date['seconds']:0);
		return true;
		//return $this->loadMembers($date);
	}/**/
	
	/**
	* sets date from mysql date format
	* @param string $date
	* @access private
	* @return bool
	*/
	function _dateTimeBean_setfrom_mysql_date($date) {
		$date=explode('-',$date);
		if (count($date)!=3) {
			trigger_error("dateTimeBean: incorrect mysql_date format : $date",E_USER_NOTICE);
			return false;
		}
		$this->setmember('mday',$date[2]);
		$this->setmember('mon',$date[1]);
		$this->setmember('year',$date[0]);
		return true;
	}/**/
	
	/**
	* sets date form mysql datetime format
	* @param string $datetime
	* @access private
	* @return bool
	*/
	function _dateTimeBean_setfrom_mysql_datetime($datetime) {
		if (strlen($datetime)!=19) return false;
		$this->seconds=intval(substr($datetime,17,2));
		$this->minutes=intval(substr($datetime,14,2));
		$this->hours=intval(substr($datetime,11,2));
		$this->mday=intval(substr($datetime,8,2));
		$this->mon=intval(substr($datetime,5,2));
		$this->year=intval(substr($datetime,0,4));
		return true;
	}/**/
	
	/**
	* sets date form mysq timestamp format
	* @param string $timestamp
	* @access private
	* @return bool
	*/
	function _dateTimeBean_setfrom_mysql_timestamp($timestamp) {
		$mysql_timestamp=strval($mysql_timestamp);
		if (!strlen($mysql_timestamp)==14) return false;
		$this->year=intval(substr($mysql_timestamp,0,4));
		$this->mon=intval(substr($mysql_timestamp,4,2));
		$this->mday=intval(substr($mysql_timestamp,6,2));
		$this->hours=intval(substr($mysql_timestamp,8,2));
		$this->minutes=intval(substr($mysql_timestamp,10,2));
		$this->seconds=intval(substr($mysql_timestamp,12,2));
		return true;
	}/**/
	
	/**
	* 
	* @param
	* @access private
	* @return 
	*/
	function _dateTimeBean_setfrom_fr_date($fr_date) {
		$date=explode("/",$fr_date);
		if (!count($date)==3) {
			trigger_error("datetimebean: format fr_date incorrect : $strdate",E_USER_NOTICE);
			return false;
		}
		//$this->seconds=0;
		//$this->minutes=0;
		//$this->hours=0;
		$this->mday=intval($date[0]);
		//$this->wday=0;
		$this->mon=intval($date[1]);
		$this->year=intval($date[2]);
		//$this->yday=0;
		//$this->weekday='';
		//$this->month' => $this->mois[intval($date[1])]);
		return true;
	}/**/
	
	/**
	* 
	* @param
	* @access private
	* @return 
	*/
	function _dateTimeBean_setFrom_iso8601($iso8601) {
		if (!is_string($iso8601) or strlen($iso8601)!=17)
			return false;
		$this->setMember('year',substr($iso8601,0,4));
		$this->setMember('mon',substr($iso8601,4,2));
		$this->setMember('mday',substr($iso8601,6,2));
		$this->setMember('hours',substr($iso8601,9,2));
		$this->setMember('minutes',substr($iso8601,12,2));
		$this->setMember('seconds',substr($iso8601,15,2));
		return true;
	}/**/
	
	/**
	* sets datetimebean from seconds
	* seconds in year : 31557600
	* @param int $seconds
	* @access private
	* @return bool
	* /
	function _dateTimeBean_setFrom_seconds($seconds) {
		//echo "setting from seconds $seconds";
		$this->reset();
		while ($seconds>31556925) {
			$this->year++;
			$seconds-=31556926;
		}
		while ($seconds>=$this->daysInMonth()*24*3600) {
			$this->mon++;
			$seconds-=$this->daysInMonth($this->year,$this->mon+1)*24*3600;
		}
		while ($seconds>86399) {
			$this->mday++;
			$seconds-=86400;
		}
		while ($seconds>3599) {
			$this->hours++;
			$seconds-=3600;
		}
		while ($seconds>59) {
			$this->minutes++;
			$seconds-=60;
		}
		
		$this->seconds=$seconds;
		//echo wakdebug::vardump($this);
		return true;
	}/**/
	
	/**
	* corrects seconds if out of range
	* @access private
	* @return bool
	*/
	function _correctSeconds() {
		if (!is_integer($this->seconds))
			return false;
		if ($this->seconds>60) {
			$this->minutes+=intval(floor($this->seconds/60));
			$this->seconds=$this->seconds%60;
		} else if ($this->seconds<0) {
			$this->minutes+=intval(floor($this->seconds/60));
			$this->seconds=$this->seconds%60;
			if ($this->seconds<0)
				$this->seconds+=60;
		}
		// echo wakapi::vardump($this);
		return $this->_correctMinutes();
	}/**/
	
	/**
	* corrects minutes if out of range
	* @access private
	* @return bool
	*/
	function _correctMinutes() {
		if (!is_integer($this->minutes))
			return false;
		if ($this->minutes>60) {
			$this->hours+=intval(floor($this->minutes/60));
			$this->minutes=$this->minutes%60;
		} else if ($this->minutes<0) {
			$this->hours+=intval(floor($this->minutes/60));
			$this->minutes=$this->minutes%60;
			if ($this->minutes<0)
				$this->minutes+=60;
		}
		// echo wakapi::vardump($this);
		return $this->_correctHours();
	}/**/
	
	/**
	* corrects hours if out of range
	* @access private
	* @return bool
	*/
	function _correctHours() {
		if (!is_integer($this->hours))
			return false;
		if ($this->hours>23) {
			$this->mday+=intval(floor($this->hours/24));
			$this->hours=$this->hours%24;
		} else if ($this->hours<0) {
			$this->mday+=intval(floor($this->hours/24));
			$this->hours=$this->hours%24;
			if ($this->hours<0)
				$this->hours+=24;
		}
		return $this->_correctMday();
	}/**/
	
	/**
	* corrects mday
	* @access private
	* @return bool
	*/
	function _correctMday() {
		if (!is_integer($this->mday))
			return false;
		while ($this->mday>$this->daysInMonth()) {
			$this->mday-=$this->daysInMonth();
			$this->mon++;
			// correcting new mon if required
			$this->_correctMon();
		}
		while ($this->mday<1) {
			$this->mon--;
			$this->mday+=$this->daysInMonth();
			// correcting new mon if required
			$this->_correctMon();
		}
		return true;
	}/**/
	
	/**
	* corrects mon
	* @access private
	* @return bool
	*/
	function _correctMon() {
		if (!is_integer($this->mon))
			return false;
		if ($this->mon>12) {
			$this->year+=intval(floor($this->mon/12));
			$this->mon=$this->mon%12;
		} else if ($this->mon<1) {
			/*$this->year-=intval(floor($this->mon/12));
			$this->mon=$this->mon%12;*/
			while ($this->mon<1) {
				$this->mon+=12;
				$this->year--;
			}
		}
		return true;
	}/**/
	
	/**
	* 
	* @param
	* @access private
	* @return 
	* /
	function () {
		
	}/**/
}
?>
